package com.xuecheng.auth.service;

import com.alibaba.fastjson.JSON;
import com.xuecheng.framework.domain.ucenter.ext.AuthToken;
import com.xuecheng.framework.domain.ucenter.response.AuthCode;
import com.xuecheng.framework.domain.ucenter.response.JwtResult;
import com.xuecheng.framework.exception.CustomExcetionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.ResponseResult;
import io.netty.handler.codec.base64.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class AuthService {
    @Autowired
    LoadBalancerClient loadBalancerClient;

    @Autowired
    RestTemplate restTemplate;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Value("${auth.tokenValiditySeconds}")
    long expires_time;


    /**
     * @Description: 登录认证
     * @Param:
     * @return:
     * @Author: Mr.Yuan
     * @Date:
     */
    public AuthToken login(String username, String password, String clientId, String clientSecret) {
//        申请令牌
        Map token = getToken(username, password, clientId, clientSecret);

        /*{access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
                eyJjb21wYW55SWQiOm51bGwsInVzZXJwaWMiOm51bGwsInVzZXJfbmFtZSI6Iml0Y2FzdCIsInNjb3BlIjpbImFwcCJdLCJuYW1lIjpudWxsLCJ1dHlwZSI6bnVsbCwiaWQiOm51bGwsImV4cCI6MTU3NDM4MTE1MCwianRpIjoiMWYzYTc1OWEtZWQ2ZS00ZThkLWJmZTItNDhlOTBiM2ZlOWFkIiwiY2xpZW50X2lkIjoiWGNXZWJBcHAifQ.
                bgcakVwE23E9-o5QbG4FvEoUrh8eoAhzXl7mjFyiM9_f_H1Kh0BUPLP1jVjicC2tsvAg3ocAenWR6n_LgFDjSYIt26aF_hAYxTPc9oYkfrU1dpt6_ziGYNzfPMUb5n66yv78s8zphwheoxjfLpZnFiF4CURcu-TEbwJkCWAOt4gL2GpKrcNbr5sNBbWaLBiOY3iKWuoIJGp39XJpGhpj2P-EB2CjtbcZ9_rSp77ztQYSq1vwCtfItoJf61LRrBK-_oDmwXg0TRPyBH9wW99ja9dRYpN00ML7505nEwTaHNDg8Fb0ExvruI2R8Kn1cAcURTLq6g-xsCNvOyTkp5oCXA, token_type=bearer,
                refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb21wYW55SWQiOm51bGwsInVzZXJwaWMiOm51bGwsInVzZXJfbmFtZSI6Iml0Y2FzdCIsInNjb3BlIjpbImFwcCJdLCJhdGkiOiIxZjNhNzU5YS1lZDZlLTRlOGQtYmZlMi00OGU5MGIzZmU5YWQiLCJuYW1lIjpudWxsLCJ1dHlwZSI6bnVsbCwiaWQiOm51bGwsImV4cCI6MTU3NDM4MTE1MCwianRpIjoiZDA3YjI2OGYtNTFlMC00Y2YwLWE5NzEtNjE0ZjlhNTdlOTNkIiwiY2xpZW50X2lkIjoiWGNXZWJBcHAifQ.Jj-A5NkWlG4YeE94Cvq_BGKWRmqSEHycM9X0X2ez_ysGRcQnqrjvrcejvIXC5iggF8jNazyE2gonEgsGeyGOvwlpdn8PjKkLGQwaCQ6YlK2OILTyebvTj0yZsaOT28z3GLwp9qmU_zBwBBzs6zVxRkVqlYjsEe4mt8Nkgnjgg3XKLpKyMrwaYQjR5f8EMO36pXDQV36pYQhRLvBqw54zVG6LSAqeIkXsbM0INa8UPLMlHSs9b5wfvtBzuimgSfqJym57CFecRRaibAhkgNEaRhfrTrRn4J5NztW3VAyUGS4J7IlQUVZghiE60vuDWBxSrf0FTzCGbK7aieMYp8PWnA,
                expires_in=43199, scope=app, jti=1f3a759a-ed6e-4e8d-bfe2-48e90b3fe9ad}*/
        if (token.get("error_description") != null) {
            String error_description = (String) token.get("error_description");
            if (error_description.contains("UserDetailsService returned null")) {
                throw CustomExcetionCast.getCusException(AuthCode.AUTH_ACCOUNT_NOTEXISTS);
            }
        }
        String token1 = (String) token.get("access_token");
        String refresh_token = (String) token.get("refresh_token");
        String jti = (String) token.get("jti");
//        int  expires_in = (int) token.get("expires_in");
        if (token == null || StringUtils.isEmpty(token1) || StringUtils.isEmpty(refresh_token) || StringUtils.isEmpty(jti)) {
            throw CustomExcetionCast.getCusException(AuthCode.AUTH_CREDENTIAL_ERROR);
        }

        AuthToken authToken = new AuthToken();
        authToken.setAccess_token(token1);
        authToken.setJwt_token(jti);
        authToken.setRefresh_token(refresh_token);
//        存入redis
        String jsonString = JSON.toJSONString(token);
        boolean b = saveRedis(jti, jsonString, expires_time);
        if (b) {
            return authToken;
        } else {
            throw CustomExcetionCast.getCusException(AuthCode.AUTH_LOGIN_ERROR);
        }
    }

    /**
     * @Description: 申请令牌私有方法
     * @Param:
     * @return:
     * @Author: Mr.Yuan
     * @Date:
     */
    private Map getToken(String username, String password, String clientId, String clientSecret) {
        //        采用客户端负载均衡，从eureka获取服务地址
        ServiceInstance instance = loadBalancerClient.choose("XC-SERVICE-UCENTER-AUTH");
//      获取远程访问地址
        URI uri = instance.getUri();
        String authString = uri + "/auth/oauth/token";
//        远程调用获取令牌
//        URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,Class<T> responseType
//       设置请求头信息
        LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>();
        String basic = clientId + ":" + clientSecret;
        byte[] encode = Base64Utils.encode(basic.getBytes());
        String baseEncod = "Basic " + new String(encode);
        header.add("Authorization", baseEncod);
//        设置请求信息
        LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "password");
        body.add("username", username);
        body.add("password", password);

        //指定 restTemplate当遇到400或401响应时候也不要抛出异常，也要正常返回值
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                //当响应的值为400或401时候也要正常响应，不要抛出异常
                if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401) {
                    super.handleError(response);
                }
            }
        });
        HttpEntity<LinkedMultiValueMap> httpEntity = new HttpEntity<LinkedMultiValueMap>(body, header);
        //        远程调用获取令牌
        ResponseEntity<Map> entity = restTemplate.exchange(authString, HttpMethod.POST, httpEntity, Map.class);
        Map body1 = entity.getBody();
        return body1;
    }

    /**
     * @Description: 存储到redis私有方法
     * @Param:
     * @return:
     * @Author: Mr.Yuan
     * @Date:
     */
    private boolean saveRedis(String accessToken, String content, long time) {
        String key = "user_token:" + accessToken;
        stringRedisTemplate.boundValueOps(key).set(content, time, TimeUnit.SECONDS);
        Long expire = stringRedisTemplate.getExpire(key, TimeUnit.SECONDS);
        return expire > 0;
    }

    /**
     * @Description: 从redis获取令牌
     * @Param:
     * @return:
     * @Author: Mr.Yuan
     * @Date:
     */
    public JwtResult getJwt(String jwt) {
        if (StringUtils.isEmpty(jwt)) {
            return null;
        }
        String jwttoken = "user_token:" + jwt;
        String value = stringRedisTemplate.opsForValue().get(jwttoken);
        if (!StringUtils.isEmpty(value)) {
            AuthToken authToken = JSON.parseObject(value, AuthToken.class);
            return new JwtResult(CommonCode.SUCCESS, authToken.getAccess_token());
        }
        return null;
    }

    /**
     * @Description: 从redis中删除cookie
     * @Param:
     * @return:
     * @Author: Mr.Yuan
     * @Date:
     */
    public ResponseResult logout(String cookieValue) {
        String key="user_token:"+cookieValue;
        Boolean delete = stringRedisTemplate.delete(key);
        if(delete){
            return new ResponseResult(CommonCode.SUCCESS);
        }else {
            return new ResponseResult(CommonCode.FAIL);
        }
    }
}
